/*-
 * Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>.
 * All rights reserved.
 * Redistribution and modifications are permitted subject to BSD license.
 */
#include <INTEGER.h>
#include <asn_codecs_prim.h> /* Encoder and decoder of a primitive type */
#include <asn_internal.h>
#include <errno.h>

/*
 * INTEGER basic type description.
 */
static ber_tlv_tag_t asn_DEF_INTEGER_tags[] = {
    (ASN_TAG_CLASS_UNIVERSAL | (2 << 2))};
asn_TYPE_descriptor_t asn_DEF_INTEGER = {
    "INTEGER",
    "INTEGER",
    ASN__PRIMITIVE_TYPE_free,
    INTEGER_print,
    asn_generic_no_constraint,
    ber_decode_primitive,
    INTEGER_encode_der,
    INTEGER_decode_xer,
    INTEGER_encode_xer,
#ifdef ASN_DISABLE_PER_SUPPORT
    0,
    0,
    0,
    0,
#else
    INTEGER_decode_uper, /* Unaligned PER decoder */
    INTEGER_encode_uper, /* Unaligned PER encoder */
    INTEGER_decode_aper,
    INTEGER_encode_aper,
#endif /* ASN_DISABLE_PER_SUPPORT */
    INTEGER_compare,
    0, /* Use generic outmost tag fetcher */
    asn_DEF_INTEGER_tags,
    sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]),
    asn_DEF_INTEGER_tags, /* Same as above */
    sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]),
    0, /* No PER visible constraints */
    0,
    0, /* No members */
    0  /* No specifics */
};

/*
 * Encode INTEGER type using DER.
 */
asn_enc_rval_t INTEGER_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
                                  int tag_mode, ber_tlv_tag_t tag,
                                  asn_app_consume_bytes_f *cb, void *app_key) {
  INTEGER_t *st = (INTEGER_t *)sptr;

  ASN_DEBUG("%s %s as INTEGER (tm=%d)", cb ? "Encoding" : "Estimating",
            td->name, tag_mode);

  /*
   * Canonicalize integer in the buffer.
   * (Remove too long sign extension, remove some first 0x00 bytes)
   */
  if (st->buf) {
    uint8_t *buf = st->buf;
    uint8_t *end1 = buf + st->size - 1;
    int shift;

    /* Compute the number of superfluous leading bytes */
    for (; buf < end1; buf++) {
      /*
       * If the contents octets of an integer value encoding
       * consist of more than one octet, then the bits of the
       * first octet and bit 8 of the second octet:
       * a) shall not all be ones; and
       * b) shall not all be zero.
       */
      switch (*buf) {
        case 0x00:
          if ((buf[1] & 0x80) == 0) continue;
          break;
        case 0xff:
          if ((buf[1] & 0x80)) continue;
          break;
      }
      break;
    }

    /* Remove leading superfluous bytes from the integer */
    shift = buf - st->buf;
    if (shift) {
      uint8_t *nb = st->buf;
      uint8_t *end;

      st->size -= shift; /* New size, minus bad bytes */
      end = nb + st->size;

      for (; nb < end; nb++, buf++) *nb = *buf;
    }

  } /* if(1) */

  return der_encode_primitive(td, sptr, tag_mode, tag, cb, app_key);
}

static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value(
    asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop);

/*
 * INTEGER specific human-readable output.
 */
static ssize_t INTEGER__dump(asn_TYPE_descriptor_t *td, const INTEGER_t *st,
                             asn_app_consume_bytes_f *cb, void *app_key,
                             int plainOrXER) {
  asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics;
  char scratch[32]; /* Enough for 64-bit integer */
  uint8_t *buf = st->buf;
  uint8_t *buf_end = st->buf + st->size;
  signed long value;
  ssize_t wrote = 0;
  char *p;
  int ret;

  if (specs && specs->field_unsigned)
    ret = asn_INTEGER2ulong(st, (unsigned long *)&value);
  else
    ret = asn_INTEGER2long(st, &value);

  /* Simple case: the integer size is small */
  if (ret == 0) {
    const asn_INTEGER_enum_map_t *el;
    size_t scrsize;
    char *scr;

    el = (value >= 0 || !specs || !specs->field_unsigned)
             ? INTEGER_map_value2enum(specs, value)
             : 0;
    if (el) {
      scrsize = el->enum_len + 32;
      scr = (char *)alloca(scrsize);
      if (plainOrXER == 0)
        ret = snprintf(scr, scrsize, "%ld (%s)", value, el->enum_name);
      else
        ret = snprintf(scr, scrsize, "<%s/>", el->enum_name);
    } else if (plainOrXER && specs && specs->strict_enumeration) {
      ASN_DEBUG(
          "ASN.1 forbids dealing with "
          "unknown value of ENUMERATED type");
      errno = EPERM;
      return -1;
    } else {
      scrsize = sizeof(scratch);
      scr = scratch;
      ret = snprintf(scr, scrsize,
                     (specs && specs->field_unsigned) ? "%lu" : "%ld", value);
    }
    assert(ret > 0 && (size_t)ret < scrsize);
    return (cb(scr, ret, app_key) < 0) ? -1 : ret;
  } else if (plainOrXER && specs && specs->strict_enumeration) {
    /*
     * Here and earlier, we cannot encode the ENUMERATED values
     * if there is no corresponding identifier.
     */
    ASN_DEBUG(
        "ASN.1 forbids dealing with "
        "unknown value of ENUMERATED type");
    errno = EPERM;
    return -1;
  }

  /* Output in the long xx:yy:zz... format */
  /* TODO: replace with generic algorithm (Knuth TAOCP Vol 2, 4.3.1) */
  for (p = scratch; buf < buf_end; buf++) {
    static const char *h2c = "0123456789ABCDEF";
    if ((p - scratch) >= (ssize_t)(sizeof(scratch) - 4)) {
      /* Flush buffer */
      if (cb(scratch, p - scratch, app_key) < 0) return -1;
      wrote += p - scratch;
      p = scratch;
    }
    *p++ = h2c[*buf >> 4];
    *p++ = h2c[*buf & 0x0F];
    *p++ = 0x3a; /* ":" */
  }
  if (p != scratch) p--; /* Remove the last ":" */

  wrote += p - scratch;
  return (cb(scratch, p - scratch, app_key) < 0) ? -1 : wrote;
}

/*
 * INTEGER specific human-readable output.
 */
int INTEGER_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
                  asn_app_consume_bytes_f *cb, void *app_key) {
  const INTEGER_t *st = (const INTEGER_t *)sptr;
  ssize_t ret;

  (void)td;
  (void)ilevel;

  if (!st || !st->buf)
    ret = cb("<absent>", 8, app_key);
  else
    ret = INTEGER__dump(td, st, cb, app_key, 0);

  return (ret < 0) ? -1 : 0;
}

struct e2v_key {
  const char *start;
  const char *stop;
  asn_INTEGER_enum_map_t *vemap;
  unsigned int *evmap;
};
static int INTEGER__compar_enum2value(const void *kp, const void *am) {
  const struct e2v_key *key = (const struct e2v_key *)kp;
  const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am;
  const char *ptr, *end, *name;

  /* Remap the element (sort by different criterion) */
  el = key->vemap + key->evmap[el - key->vemap];

  /* Compare strings */
  for (ptr = key->start, end = key->stop, name = el->enum_name; ptr < end;
       ptr++, name++) {
    if (*ptr != *name)
      return *(const unsigned char *)ptr - *(const unsigned char *)name;
  }
  return name[0] ? -1 : 0;
}

static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value(
    asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop) {
  asn_INTEGER_enum_map_t *el_found;
  int count = specs ? specs->map_count : 0;
  struct e2v_key key;
  const char *lp;

  if (!count) return NULL;

  /* Guaranteed: assert(lstart < lstop); */
  /* Figure out the tag name */
  for (lstart++, lp = lstart; lp < lstop; lp++) {
    switch (*lp) {
      case 9:
      case 10:
      case 11:
      case 12:
      case 13:
      case 32:   /* WSP */
      case 0x2f: /* '/' */
      case 0x3e: /* '>' */
        break;
      default:
        continue;
    }
    break;
  }
  if (lp == lstop) return NULL; /* No tag found */
  lstop = lp;

  key.start = lstart;
  key.stop = lstop;
  key.vemap = specs->value2enum;
  key.evmap = specs->enum2value;
  el_found = (asn_INTEGER_enum_map_t *)bsearch(&key, specs->value2enum, count,
                                               sizeof(specs->value2enum[0]),
                                               INTEGER__compar_enum2value);
  if (el_found) {
    /* Remap enum2value into value2enum */
    el_found = key.vemap + key.evmap[el_found - key.vemap];
  }
  return el_found;
}

static int INTEGER__compar_value2enum(const void *kp, const void *am) {
  long a = *(const long *)kp;
  const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am;
  long b = el->nat_value;
  if (a < b)
    return -1;
  else if (a == b)
    return 0;
  else
    return 1;
}

const asn_INTEGER_enum_map_t *INTEGER_map_value2enum(
    asn_INTEGER_specifics_t *specs, long value) {
  int count = specs ? specs->map_count : 0;
  if (!count) return 0;
  return (asn_INTEGER_enum_map_t *)bsearch(&value, specs->value2enum, count,
                                           sizeof(specs->value2enum[0]),
                                           INTEGER__compar_value2enum);
}

static int INTEGER_st_prealloc(INTEGER_t *st, int min_size) {
  void *p = MALLOC(min_size + 1);
  if (p) {
    void *b = st->buf;
    st->size = 0;
    st->buf = p;
    FREEMEM(b);
    return 0;
  } else {
    return -1;
  }
}

/*
 * Decode the chunk of XML text encoding INTEGER.
 */
static enum xer_pbd_rval INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td,
                                                  void *sptr,
                                                  const void *chunk_buf,
                                                  size_t chunk_size) {
  INTEGER_t *st = (INTEGER_t *)sptr;
  long dec_value;
  long hex_value = 0;
  const char *lp;
  const char *lstart = (const char *)chunk_buf;
  const char *lstop = lstart + chunk_size;
  enum {
    ST_LEADSPACE,
    ST_SKIPSPHEX,
    ST_WAITDIGITS,
    ST_DIGITS,
    ST_DIGITS_TRAILSPACE,
    ST_HEXDIGIT1,
    ST_HEXDIGIT2,
    ST_HEXDIGITS_TRAILSPACE,
    ST_HEXCOLON,
    ST_END_ENUM,
    ST_UNEXPECTED
  } state = ST_LEADSPACE;
  const char *dec_value_start = 0; /* INVARIANT: always !0 in ST_DIGITS */
  const char *dec_value_end = 0;

  if (chunk_size)
    ASN_DEBUG("INTEGER body %ld 0x%2x..0x%2x", (long)chunk_size, *lstart,
              lstop[-1]);

  if (INTEGER_st_prealloc(st, (chunk_size / 3) + 1)) return XPBD_SYSTEM_FAILURE;

  /*
   * We may have received a tag here. It will be processed inline.
   * Use strtoul()-like code and serialize the result.
   */
  for (lp = lstart; lp < lstop; lp++) {
    int lv = *lp;
    switch (lv) {
      case 0x09:
      case 0x0a:
      case 0x0d:
      case 0x20:
        switch (state) {
          case ST_LEADSPACE:
          case ST_DIGITS_TRAILSPACE:
          case ST_HEXDIGITS_TRAILSPACE:
          case ST_SKIPSPHEX:
            continue;
          case ST_DIGITS:
            dec_value_end = lp;
            state = ST_DIGITS_TRAILSPACE;
            continue;
          case ST_HEXCOLON:
            state = ST_HEXDIGITS_TRAILSPACE;
            continue;
          default:
            break;
        }
        break;
      case 0x2d: /* '-' */
        if (state == ST_LEADSPACE) {
          dec_value = 0;
          dec_value_start = lp;
          state = ST_WAITDIGITS;
          continue;
        }
        break;
      case 0x2b: /* '+' */
        if (state == ST_LEADSPACE) {
          dec_value = 0;
          dec_value_start = lp;
          state = ST_WAITDIGITS;
          continue;
        }
        break;
      case 0x30:
      case 0x31:
      case 0x32:
      case 0x33:
      case 0x34:
      case 0x35:
      case 0x36:
      case 0x37:
      case 0x38:
      case 0x39:
        switch (state) {
          case ST_DIGITS:
            continue;
          case ST_SKIPSPHEX: /* Fall through */
          case ST_HEXDIGIT1:
            hex_value = (lv - 0x30) << 4;
            state = ST_HEXDIGIT2;
            continue;
          case ST_HEXDIGIT2:
            hex_value += (lv - 0x30);
            state = ST_HEXCOLON;
            st->buf[st->size++] = (uint8_t)hex_value;
            continue;
          case ST_HEXCOLON:
            return XPBD_BROKEN_ENCODING;
          case ST_LEADSPACE:
            dec_value = 0;
            dec_value_start = lp;
            /* FALL THROUGH */
          case ST_WAITDIGITS:
            state = ST_DIGITS;
            continue;
          default:
            break;
        }
        break;
      case 0x3c: /* '<', start of XML encoded enumeration */
        if (state == ST_LEADSPACE) {
          const asn_INTEGER_enum_map_t *el;
          el = INTEGER_map_enum2value((asn_INTEGER_specifics_t *)td->specifics,
                                      lstart, lstop);
          if (el) {
            ASN_DEBUG("Found \"%s\" => %ld", el->enum_name, el->nat_value);
            dec_value = el->nat_value;
            state = ST_END_ENUM;
            lp = lstop - 1;
            continue;
          }
          ASN_DEBUG("Unknown identifier for INTEGER");
        }
        return XPBD_BROKEN_ENCODING;
      case 0x3a: /* ':' */
        if (state == ST_HEXCOLON) {
          /* This colon is expected */
          state = ST_HEXDIGIT1;
          continue;
        } else if (state == ST_DIGITS) {
          /* The colon here means that we have
           * decoded the first two hexadecimal
           * places as a decimal value.
           * Switch decoding mode. */
          ASN_DEBUG("INTEGER re-evaluate as hex form");
          state = ST_SKIPSPHEX;
          dec_value_start = 0;
          lp = lstart - 1;
          continue;
        } else {
          ASN_DEBUG("state %d at %ld", state, (long)(lp - lstart));
          break;
        }
      /* [A-Fa-f] */
      case 0x41:
      case 0x42:
      case 0x43:
      case 0x44:
      case 0x45:
      case 0x46:
      case 0x61:
      case 0x62:
      case 0x63:
      case 0x64:
      case 0x65:
      case 0x66:
        switch (state) {
          case ST_SKIPSPHEX:
          case ST_LEADSPACE: /* Fall through */
          case ST_HEXDIGIT1:
            hex_value = lv - ((lv < 0x61) ? 0x41 : 0x61);
            hex_value += 10;
            hex_value <<= 4;
            state = ST_HEXDIGIT2;
            continue;
          case ST_HEXDIGIT2:
            hex_value += lv - ((lv < 0x61) ? 0x41 : 0x61);
            hex_value += 10;
            st->buf[st->size++] = (uint8_t)hex_value;
            state = ST_HEXCOLON;
            continue;
          case ST_DIGITS:
            ASN_DEBUG("INTEGER re-evaluate as hex form");
            state = ST_SKIPSPHEX;
            dec_value_start = 0;
            lp = lstart - 1;
            continue;
          default:
            break;
        }
        break;
    }

    /* Found extra non-numeric stuff */
    ASN_DEBUG("INTEGER :: Found non-numeric 0x%2x at %ld", lv,
              (long)(lp - lstart));
    state = ST_UNEXPECTED;
    break;
  }

  switch (state) {
    case ST_END_ENUM:
      /* Got a complete and valid enumeration encoded as a tag. */
      break;
    case ST_DIGITS:
      dec_value_end = lstop;
      /* FALL THROUGH */
    case ST_DIGITS_TRAILSPACE:
      /* The last symbol encountered was a digit. */
      switch (asn_strtol_lim(dec_value_start, &dec_value_end, &dec_value)) {
        case ASN_STRTOL_OK:
          break;
        case ASN_STRTOL_ERROR_RANGE:
          return XPBD_DECODER_LIMIT;
        case ASN_STRTOL_ERROR_INVAL:
        case ASN_STRTOL_EXPECT_MORE:
        case ASN_STRTOL_EXTRA_DATA:
          return XPBD_BROKEN_ENCODING;
      }
      break;
    case ST_HEXCOLON:
    case ST_HEXDIGITS_TRAILSPACE:
      st->buf[st->size] = 0; /* Just in case termination */
      return XPBD_BODY_CONSUMED;
    case ST_HEXDIGIT1:
    case ST_HEXDIGIT2:
    case ST_SKIPSPHEX:
      return XPBD_BROKEN_ENCODING;
    case ST_LEADSPACE:
      /* Content not found */
      return XPBD_NOT_BODY_IGNORE;
    case ST_WAITDIGITS:
    case ST_UNEXPECTED:
      ASN_DEBUG("INTEGER: No useful digits (state %d)", state);
      return XPBD_BROKEN_ENCODING; /* No digits */
  }

  /*
   * Convert the result of parsing of enumeration or a straight
   * decimal value into a BER representation.
   */
  if (asn_long2INTEGER(st, dec_value)) return XPBD_SYSTEM_FAILURE;

  return XPBD_BODY_CONSUMED;
}

asn_dec_rval_t INTEGER_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
                                  asn_TYPE_descriptor_t *td, void **sptr,
                                  const char *opt_mname, const void *buf_ptr,
                                  size_t size) {
  return xer_decode_primitive(opt_codec_ctx, td, sptr, sizeof(INTEGER_t),
                              opt_mname, buf_ptr, size,
                              INTEGER__xer_body_decode);
}

asn_enc_rval_t INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
                                  int ilevel, enum xer_encoder_flags_e flags,
                                  asn_app_consume_bytes_f *cb, void *app_key) {
  const INTEGER_t *st = (const INTEGER_t *)sptr;
  asn_enc_rval_t er;

  (void)ilevel;
  (void)flags;

  if (!st || !st->buf) _ASN_ENCODE_FAILED;

  er.encoded = INTEGER__dump(td, st, cb, app_key, 1);
  if (er.encoded < 0) _ASN_ENCODE_FAILED;

  _ASN_ENCODED_OK(er);
}

#ifndef ASN_DISABLE_PER_SUPPORT

asn_dec_rval_t INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
                                   asn_TYPE_descriptor_t *td,
                                   asn_per_constraints_t *constraints,
                                   void **sptr, asn_per_data_t *pd) {
  asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics;
  asn_dec_rval_t rval = {RC_OK, 0};
  INTEGER_t *st = (INTEGER_t *)*sptr;
  asn_per_constraint_t *ct;
  int repeat;

  (void)opt_codec_ctx;

  if (!st) {
    st = (INTEGER_t *)(*sptr = CALLOC(1, sizeof(*st)));
    if (!st) _ASN_DECODE_FAILED;
  }

  if (!constraints) constraints = td->per_constraints;
  ct = constraints ? &constraints->value : 0;

  if (ct && ct->flags & APC_EXTENSIBLE) {
    int inext = per_get_few_bits(pd, 1);
    if (inext < 0) _ASN_DECODE_STARVED;
    if (inext) ct = 0;
  }

  FREEMEM(st->buf);
  st->buf = 0;
  st->size = 0;
  if (ct) {
    if (ct->flags & APC_SEMI_CONSTRAINED) {
      st->buf = (uint8_t *)CALLOC(1, 2);
      if (!st->buf) _ASN_DECODE_FAILED;
      st->size = 1;
    } else if (ct->flags & APC_CONSTRAINED && ct->range_bits >= 0) {
      size_t size = (ct->range_bits + 7) >> 3;
      st->buf = (uint8_t *)MALLOC(1 + size + 1);
      if (!st->buf) _ASN_DECODE_FAILED;
      st->size = size;
    }
  }

  /* X.691, #12.2.2 */
  if (ct && ct->flags != APC_UNCONSTRAINED) {
    /* #10.5.6 */
    ASN_DEBUG("Integer with range %d bits", ct->range_bits);
    if (ct->range_bits >= 0) {
      long value;
      if (ct->range_bits == 32) {
        long lhalf;
        value = per_get_few_bits(pd, 16);
        if (value < 0) _ASN_DECODE_STARVED;
        lhalf = per_get_few_bits(pd, 16);
        if (lhalf < 0) _ASN_DECODE_STARVED;
        value = (value << 16) | lhalf;
      } else {
        value = per_get_few_bits(pd, ct->range_bits);
        if (value < 0) _ASN_DECODE_STARVED;
      }
      ASN_DEBUG("Got value %ld + low %" PRIdMAX, value, ct->lower_bound);
      value += ct->lower_bound;
      if ((specs && specs->field_unsigned) ? asn_ulong2INTEGER(st, value)
                                           : asn_long2INTEGER(st, value))
        _ASN_DECODE_FAILED;
      return rval;
    }
  } else {
    ASN_DEBUG("Decoding unconstrained integer %s", td->name);
  }

  /* X.691, #12.2.3, #12.2.4 */
  do {
    ssize_t len;
    void *p;
    int ret;

    /* Get the PER length */
    len = uper_get_length(pd, -1, &repeat);
    if (len < 0) _ASN_DECODE_STARVED;

    p = REALLOC(st->buf, st->size + len + 1);
    if (!p) _ASN_DECODE_FAILED;
    st->buf = (uint8_t *)p;

    ret = per_get_many_bits(pd, &st->buf[st->size], 0, 8 * len);
    if (ret < 0) _ASN_DECODE_STARVED;
    st->size += len;
  } while (repeat);
  st->buf[st->size] = 0; /* JIC */

  /* #12.2.3 */
  if (ct && ct->lower_bound) {
    /*
     * TODO: replace by in-place arithmetics.
     */
    long value;
    if (asn_INTEGER2long(st, &value)) _ASN_DECODE_FAILED;
    if (asn_long2INTEGER(st, value + ct->lower_bound)) _ASN_DECODE_FAILED;
  }

  return rval;
}

asn_dec_rval_t INTEGER_decode_aper(asn_codec_ctx_t *opt_codec_ctx,
                                   asn_TYPE_descriptor_t *td,
                                   asn_per_constraints_t *constraints,
                                   void **sptr, asn_per_data_t *pd) {
  asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics;
  asn_dec_rval_t rval = {RC_OK, 0};
  INTEGER_t *st = (INTEGER_t *)*sptr;
  asn_per_constraint_t *ct;
  int repeat;

  (void)opt_codec_ctx;

  if (!st) {
    st = (INTEGER_t *)(*sptr = CALLOC(1, sizeof(*st)));
    if (!st) _ASN_DECODE_FAILED;
  }

  if (!constraints) constraints = td->per_constraints;
  ct = constraints ? &constraints->value : 0;

  if (ct && ct->flags & APC_EXTENSIBLE) {
    int inext = per_get_few_bits(pd, 1);
    if (inext < 0) _ASN_DECODE_STARVED;
    if (inext) ct = 0;
  }

  FREEMEM(st->buf);
  st->buf = 0;
  st->size = 0;
  if (ct) {
    if (ct->flags & APC_SEMI_CONSTRAINED) {
      st->buf = (uint8_t *)CALLOC(1, 2);
      if (!st->buf) _ASN_DECODE_FAILED;
      st->size = 1;
    } else if (ct->flags & APC_CONSTRAINED && ct->range_bits >= 0) {
      size_t size = (ct->range_bits + 7) >> 3;
      st->buf = (uint8_t *)MALLOC(1 + size + 1);
      if (!st->buf) _ASN_DECODE_FAILED;
      st->size = size;
    }
  }

  /* X.691, #12.2.2 */
  if (ct && ct->flags != APC_UNCONSTRAINED) {
    /* #10.5.6 */
    ASN_DEBUG("Integer with range %d bits", ct->range_bits);
    if (ct->range_bits >= 0) {
      if (ct->range_bits > 16) {
        int max_range_bytes =
            (ct->range_bits >> 3) + (((ct->range_bits % 8) > 0) ? 1 : 0);
        int length = 0, i;
        int64_t value = 0;

        for (i = 1;; i++) {
          int upper = 1 << i;
          if (upper >= max_range_bytes) break;
        }
        ASN_DEBUG("Can encode %d (%d bytes) in %d bits", ct->range_bits,
                  max_range_bytes, i);

        if ((length = per_get_few_bits(pd, i)) < 0) _ASN_DECODE_FAILED;

        /* X.691 #12.2.6 length determinant + lb (1) */
        length += 1;
        ASN_DEBUG("Got length %d", length);

        if (aper_get_align(pd) != 0) _ASN_DECODE_STARVED;

        while (length--) {
          int buf = per_get_few_bits(pd, 8);
          if (buf < 0) _ASN_DECODE_STARVED;
          value += (((int64_t)buf) << (8 * length));
        }

        value += ct->lower_bound;
        if ((specs && specs->field_unsigned) ? asn_uint642INTEGER(st, value)
                                             : asn_int642INTEGER(st, value))
          _ASN_DECODE_FAILED;
        ASN_DEBUG("Got value %lld + low %lld", value, ct->lower_bound);
      } else {
        long value = 0;
        if (ct->range_bits < 8) {
          value = per_get_few_bits(pd, ct->range_bits);
          if (value < 0) _ASN_DECODE_STARVED;
        } else if (ct->range_bits == 8) {
          if (aper_get_align(pd) < 0) _ASN_DECODE_FAILED;
          value = per_get_few_bits(pd, ct->range_bits);
          if (value < 0) _ASN_DECODE_STARVED;
        } else {
          /* Align */
          if (aper_get_align(pd) < 0) _ASN_DECODE_FAILED;
          value = per_get_few_bits(pd, 16);
          if (value < 0) _ASN_DECODE_STARVED;
        }
        value += ct->lower_bound;
        if ((specs && specs->field_unsigned) ? asn_ulong2INTEGER(st, value)
                                             : asn_long2INTEGER(st, value))
          _ASN_DECODE_FAILED;
        ASN_DEBUG("Got value %ld + low %lld", value, ct->lower_bound);
      }
      return rval;
    } else {
      _ASN_DECODE_FAILED;
    }
  } else {
    ASN_DEBUG("Decoding unconstrained integer %s", td->name);
  }

  /* X.691, #12.2.3, #12.2.4 */
  do {
    ssize_t len;
    void *p;
    int ret;

    /* Get the PER length */
    len = aper_get_length(pd, -1, -1, &repeat);
    if (len < 0) _ASN_DECODE_STARVED;

    p = REALLOC(st->buf, st->size + len + 1);
    if (!p) _ASN_DECODE_FAILED;
    st->buf = (uint8_t *)p;

    ret = per_get_many_bits(pd, &st->buf[st->size], 0, 8 * len);
    if (ret < 0) _ASN_DECODE_STARVED;
    st->size += len;
  } while (repeat);
  st->buf[st->size] = 0; /* JIC */

  /* #12.2.3 */
  if (ct && ct->lower_bound) {
    /*
     * TODO: replace by in-place arithmetics.
     */
    long value;
    if (asn_INTEGER2long(st, &value)) _ASN_DECODE_FAILED;
    if (asn_long2INTEGER(st, value + ct->lower_bound)) _ASN_DECODE_FAILED;
  }

  return rval;
}

asn_enc_rval_t INTEGER_encode_uper(asn_TYPE_descriptor_t *td,
                                   asn_per_constraints_t *constraints,
                                   void *sptr, asn_per_outp_t *po) {
  asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics;
  asn_enc_rval_t er;
  INTEGER_t *st = (INTEGER_t *)sptr;
  const uint8_t *buf;
  const uint8_t *end;
  asn_per_constraint_t *ct;
  long value = 0;

  if (!st || st->size == 0) _ASN_ENCODE_FAILED;

  if (!constraints) constraints = td->per_constraints;
  ct = constraints ? &constraints->value : 0;

  er.encoded = 0;

  if (ct) {
    int inext = 0;
    if (specs && specs->field_unsigned) {
      unsigned long uval;
      if (asn_INTEGER2ulong(st, &uval)) _ASN_ENCODE_FAILED;
      /* Check proper range */
      if (ct->flags & APC_SEMI_CONSTRAINED) {
        if (uval < (unsigned long)ct->lower_bound) inext = 1;
      } else if (ct->range_bits >= 0) {
        if (uval < (unsigned long)ct->lower_bound ||
            uval > (unsigned long)ct->upper_bound)
          inext = 1;
      }
      ASN_DEBUG("Value %lu (%02x/%d) lb %" PRIuMAX " ub %" PRIuMAX " %s", uval,
                st->buf[0], st->size, ct->lower_bound, ct->upper_bound,
                inext ? "ext" : "fix");
      value = uval;
    } else {
      if (asn_INTEGER2long(st, &value)) _ASN_ENCODE_FAILED;
      /* Check proper range */
      if (ct->flags & APC_SEMI_CONSTRAINED) {
        if (value < ct->lower_bound) inext = 1;
      } else if (ct->range_bits >= 0) {
        if (value < ct->lower_bound || value > ct->upper_bound) inext = 1;
      }
      ASN_DEBUG("Value %ld (%02x/%d) lb %" PRIdMAX " ub %" PRIdMAX " %s", value,
                st->buf[0], st->size, ct->lower_bound, ct->upper_bound,
                inext ? "ext" : "fix");
    }
    if (ct->flags & APC_EXTENSIBLE) {
      if (per_put_few_bits(po, inext, 1)) _ASN_ENCODE_FAILED;
      if (inext) ct = 0;
    } else if (inext) {
      _ASN_ENCODE_FAILED;
    }
  }

  /* X.691, #12.2.2 */
  if (ct && ct->range_bits >= 0) {
    /* #10.5.6 */
    ASN_DEBUG("Encoding integer with range %d bits", ct->range_bits);
    if (ct->range_bits == 32) {
      /* TODO: extend to >32 bits */
      long v = value - ct->lower_bound;
      if (per_put_few_bits(po, v >> 1, 31) || per_put_few_bits(po, v, 1))
        _ASN_ENCODE_FAILED;
    } else {
      if (per_put_few_bits(po, value - ct->lower_bound, ct->range_bits))
        _ASN_ENCODE_FAILED;
    }
    _ASN_ENCODED_OK(er);
  }

  if (ct && ct->lower_bound) {
    ASN_DEBUG("Adjust lower bound to %" PRIdMAX "\n", ct->lower_bound);
    /* TODO: adjust lower bound */
    _ASN_ENCODE_FAILED;
  }

  for (buf = st->buf, end = st->buf + st->size; buf < end;) {
    ssize_t mayEncode = uper_put_length(po, end - buf);
    if (mayEncode < 0) _ASN_ENCODE_FAILED;
    if (per_put_many_bits(po, buf, 8 * mayEncode)) _ASN_ENCODE_FAILED;
    buf += mayEncode;
  }

  _ASN_ENCODED_OK(er);
}

asn_enc_rval_t INTEGER_encode_aper(asn_TYPE_descriptor_t *td,
                                   asn_per_constraints_t *constraints,
                                   void *sptr, asn_per_outp_t *po) {
  asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics;
  asn_enc_rval_t er;
  INTEGER_t *st = (INTEGER_t *)sptr;
  const uint8_t *buf;
  const uint8_t *end;
  asn_per_constraint_t *ct;
  int64_t value = 0;

  if (!st || st->size == 0) _ASN_ENCODE_FAILED;

  if (!constraints) constraints = td->per_constraints;
  ct = constraints ? &constraints->value : 0;

  er.encoded = 0;

  if (ct) {
    int inext = 0;
    if (specs && specs->field_unsigned) {
      uint64_t uval;
      if (asn_INTEGER2uint64(st, &uval)) _ASN_ENCODE_FAILED;
      /* Check proper range */
      if (ct->flags & APC_SEMI_CONSTRAINED) {
        if (uval < ct->lower_bound) inext = 1;
      } else if (ct->range_bits >= 0) {
        if (uval < ct->lower_bound || uval > ct->upper_bound) inext = 1;
      }
      ASN_DEBUG("Value %llu (%02x/%d) lb %llu ub %llu %s", uval, st->buf[0],
                st->size, ct->lower_bound, ct->upper_bound,
                inext ? "ext" : "fix");
      value = uval;
    } else {
      if (asn_INTEGER2int64(st, &value)) _ASN_ENCODE_FAILED;
      /* Check proper range */
      if (ct->flags & APC_SEMI_CONSTRAINED) {
        if (value < ct->lower_bound) inext = 1;
      } else if (ct->range_bits >= 0) {
        if (value < ct->lower_bound || value > ct->upper_bound) inext = 1;
      }
      ASN_DEBUG("Value %lld (%02x/%d) lb %lld ub %lld %s", value, st->buf[0],
                st->size, ct->lower_bound, ct->upper_bound,
                inext ? "ext" : "fix");
    }
    if (ct->flags & APC_EXTENSIBLE) {
      if (per_put_few_bits(po, inext, 1)) _ASN_ENCODE_FAILED;
      if (inext) ct = 0;
    } else if (inext) {
      _ASN_ENCODE_FAILED;
    }
  }

  /* X.691, #12.2.2 */
  if (ct && ct->range_bits >= 0) {
    /* #10.5.6 */
    ASN_DEBUG("Encoding integer with range %d bits", ct->range_bits);

    value -= ct->lower_bound;

    /* #12 <= 8 -> alignment ? */
    if (ct->range_bits < 8) {
      /* Bit field case -> no alignment*/
      if (per_put_few_bits(po, 0x00 | value, ct->range_bits))
        _ASN_ENCODE_FAILED;
    } else if (ct->range_bits == 8) {
      if (aper_put_align(po) < 0) _ASN_ENCODE_FAILED;
      if (per_put_few_bits(po, 0x00 | value, ct->range_bits))
        _ASN_ENCODE_FAILED;
    } else if (ct->range_bits <= 16) {
      // Consume the bytes to align on octet
      if (aper_put_align(po) < 0) _ASN_ENCODE_FAILED;
      if (per_put_few_bits(po, 0x0000 | value, 16)) _ASN_ENCODE_FAILED;
    } else {
      /* TODO: extend to >64 bits */
      int64_t v = value;
      int i, j;
      int max_range_bytes =
          (ct->range_bits >> 3) + (((ct->range_bits % 8) > 0) ? 1 : 0);

      for (i = 1;; i++) {
        int upper = 1 << i;
        if (upper >= max_range_bytes) break;
      }

      for (j = sizeof(int64_t) - 1; j != 0; j--) {
        uint8_t val;
        val = v >> (j * 8);
        if (val != 0) {
          break;
        }
      }

      ASN_DEBUG("Putting n - lb (%" PRIdMAX
                ") with range %d bytes (real size - lb (1)) %d in %d bits",
                v, max_range_bytes, j, i);

      /* Putting length in the minimum number of bits ex: 5 = 3bits */
      if (per_put_few_bits(po, j, i)) _ASN_ENCODE_FAILED;

      // Consume the bits to align on octet
      if (aper_put_align(po) < 0) _ASN_ENCODE_FAILED;

      /* Put the value */
      for (i = 0; i <= j; i++) {
        if (per_put_few_bits(po, (v >> (8 * (j - i))) & 0xff, 8))
          _ASN_ENCODE_FAILED;
      }
    }
    _ASN_ENCODED_OK(er);
  }

  if (ct && ct->lower_bound) {
    ASN_DEBUG("Adjust lower bound to %" PRIdMAX, ct->lower_bound);
    /* TODO: adjust lower bound */
    _ASN_ENCODE_FAILED;
  }

  for (buf = st->buf, end = st->buf + st->size; buf < end;) {
    ssize_t mayEncode = aper_put_length(po, -1, end - buf);
    if (mayEncode < 0) _ASN_ENCODE_FAILED;
    if (per_put_many_bits(po, buf, 8 * mayEncode)) _ASN_ENCODE_FAILED;
    buf += mayEncode;
  }

  _ASN_ENCODED_OK(er);
}

#endif /* ASN_DISABLE_PER_SUPPORT */

int asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) {
  uint8_t *b, *end;
  size_t size;
  long l;

  /* Sanity checking */
  if (!iptr || !iptr->buf || !lptr) {
    errno = EINVAL;
    return -1;
  }

  /* Cache the begin/end of the buffer */
  b = iptr->buf; /* Start of the INTEGER buffer */
  size = iptr->size;
  end = b + size; /* Where to stop */

  if (size > sizeof(long)) {
    uint8_t *end1 = end - 1;
    /*
     * Slightly more advanced processing,
     * able to >sizeof(long) bytes,
     * when the actual value is small
     * (0x0000000000abcdef would yield a fine 0x00abcdef)
     */
    /* Skip out the insignificant leading bytes */
    for (; b < end1; b++) {
      switch (*b) {
        case 0x00:
          if ((b[1] & 0x80) == 0) continue;
          break;
        case 0xff:
          if ((b[1] & 0x80) != 0) continue;
          break;
      }
      break;
    }

    size = end - b;
    if (size > sizeof(long)) {
      /* Still cannot fit the long */
      errno = ERANGE;
      return -1;
    }
  }

  /* Shortcut processing of a corner case */
  if (end == b) {
    *lptr = 0;
    return 0;
  }

  /* Perform the sign initialization */
  /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */
  if ((*b >> 7))
    l = -1;
  else
    l = 0;

  /* Conversion engine */
  for (; b < end; b++) l = (l << 8) | *b;

  *lptr = l;
  return 0;
}

int asn_INTEGER2int64(const INTEGER_t *iptr, int64_t *lptr) {
  uint8_t *b, *end;
  size_t size;
  int64_t l;

  /* Sanity checking */
  if (!iptr || !iptr->buf || !lptr) {
    errno = EINVAL;
    return -1;
  }

  /* Cache the begin/end of the buffer */
  b = iptr->buf; /* Start of the INTEGER buffer */
  size = iptr->size;
  end = b + size; /* Where to stop */

  if (size > sizeof(int64_t)) {
    uint8_t *end1 = end - 1;
    /*
     * Slightly more advanced processing,
     * able to >sizeof(int64_t) bytes,
     * when the actual value is small
     * (0x0000000000abcdef would yield a fine 0x00abcdef)
     */
    /* Skip out the insignificant leading bytes */
    for (; b < end1; b++) {
      switch (*b) {
        case 0x00:
          if ((b[1] & 0x80) == 0) continue;
          break;
        case 0xff:
          if ((b[1] & 0x80) != 0) continue;
          break;
      }
      break;
    }

    size = end - b;
    if (size > sizeof(int64_t)) {
      /* Still cannot fit the int64_t */
      errno = ERANGE;
      return -1;
    }
  }

  /* Shortcut processing of a corner case */
  if (end == b) {
    *lptr = 0;
    return 0;
  }

  /* Perform the sign initialization */
  /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */
  if ((*b >> 7))
    l = -1;
  else
    l = 0;

  /* Conversion engine */
  for (; b < end; b++) l = (l << 8) | *b;

  *lptr = l;
  return 0;
}

int asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *lptr) {
  uint8_t *b, *end;
  unsigned long l;
  size_t size;

  if (!iptr || !iptr->buf || !lptr) {
    errno = EINVAL;
    return -1;
  }

  b = iptr->buf;
  size = iptr->size;
  end = b + size;

  /* If all extra leading bytes are zeroes, ignore them */
  for (; size > sizeof(unsigned long); b++, size--) {
    if (*b) {
      /* Value won't fit unsigned long */
      errno = ERANGE;
      return -1;
    }
  }

  /* Conversion engine */
  for (l = 0; b < end; b++) l = (l << 8) | *b;

  *lptr = l;
  return 0;
}

int asn_INTEGER2uint64(const INTEGER_t *iptr, uint64_t *lptr) {
  uint8_t *b, *end;
  uint64_t l;
  size_t size;

  if (!iptr || !iptr->buf || !lptr) {
    errno = EINVAL;
    return -1;
  }

  b = iptr->buf;
  size = iptr->size;
  end = b + size;

  /* If all extra leading bytes are zeroes, ignore them */
  for (; size > sizeof(uint64_t); b++, size--) {
    if (*b) {
      /* Value won't fit unsigned long */
      errno = ERANGE;
      return -1;
    }
  }

  /* Conversion engine */
  for (l = 0; b < end; b++) l = (l << 8) | *b;

  *lptr = l;
  return 0;
}

int asn_ulong2INTEGER(INTEGER_t *st, unsigned long value) {
  uint8_t *buf;
  uint8_t *end;
  uint8_t *b;
  int shr;

  if (value <= LONG_MAX) return asn_long2INTEGER(st, value);

  buf = (uint8_t *)MALLOC(1 + sizeof(value));
  if (!buf) return -1;

  end = buf + (sizeof(value) + 1);
  buf[0] = 0;
  for (b = buf + 1, shr = (sizeof(unsigned long) - 1) * 8; b < end;
       shr -= 8, b++)
    *b = (uint8_t)(value >> shr);

  if (st->buf) FREEMEM(st->buf);
  st->buf = buf;
  st->size = 1 + sizeof(value);

  return 0;
}

int asn_uint642INTEGER(INTEGER_t *st, uint64_t value) {
  uint8_t *buf;
  uint8_t *end;
  uint8_t *b;
  int shr;

  if (value <= INT64_MAX) return asn_int642INTEGER(st, value);

  buf = (uint8_t *)MALLOC(1 + sizeof(value));
  if (!buf) return -1;

  end = buf + (sizeof(value) + 1);
  buf[0] = 0;
  for (b = buf + 1, shr = (sizeof(value) - 1) * 8; b < end; shr -= 8, b++)
    *b = (uint8_t)(value >> shr);

  if (st->buf) FREEMEM(st->buf);
  st->buf = buf;
  st->size = 1 + sizeof(value);

  return 0;
}

int asn_long2INTEGER(INTEGER_t *st, long value) {
  uint8_t *buf, *bp;
  uint8_t *p;
  uint8_t *pstart;
  uint8_t *pend1;
  int littleEndian = 1; /* Run-time detection */
  int add;

  if (!st) {
    errno = EINVAL;
    return -1;
  }

  buf = (uint8_t *)MALLOC(sizeof(value));
  if (!buf) return -1;

  if (*(char *)&littleEndian) {
    pstart = (uint8_t *)&value + sizeof(value) - 1;
    pend1 = (uint8_t *)&value;
    add = -1;
  } else {
    pstart = (uint8_t *)&value;
    pend1 = pstart + sizeof(value) - 1;
    add = 1;
  }

  /*
   * If the contents octet consists of more than one octet,
   * then bits of the first octet and bit 8 of the second octet:
   * a) shall not all be ones; and
   * b) shall not all be zero.
   */
  for (p = pstart; p != pend1; p += add) {
    switch (*p) {
      case 0x00:
        if ((*(p + add) & 0x80) == 0) continue;
        break;
      case 0xff:
        if ((*(p + add) & 0x80)) continue;
        break;
    }
    break;
  }
  /* Copy the integer body */
  for (pstart = p, bp = buf, pend1 += add; p != pend1; p += add) *bp++ = *p;

  if (st->buf) FREEMEM(st->buf);
  st->buf = buf;
  st->size = bp - buf;

  return 0;
}

/*
 * This function is going to be DEPRECATED soon.
 */
enum asn_strtol_result_e asn_strtol(const char *str, const char *end,
                                    long *lp) {
  const char *endp = end;

  switch (asn_strtol_lim(str, &endp, lp)) {
    case ASN_STRTOL_ERROR_RANGE:
      return ASN_STRTOL_ERROR_RANGE;
    case ASN_STRTOL_ERROR_INVAL:
      return ASN_STRTOL_ERROR_INVAL;
    case ASN_STRTOL_EXPECT_MORE:
      return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */
    case ASN_STRTOL_OK:
      return ASN_STRTOL_OK;
    case ASN_STRTOL_EXTRA_DATA:
      return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */
  }

  return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */
}

/*
 * Parse the number in the given string until the given *end position,
 * returning the position after the last parsed character back using the
 * same (*end) pointer.
 * WARNING: This behavior is different from the standard strtol(3).
 */
enum asn_strtol_result_e asn_strtol_lim(const char *str, const char **end,
                                        long *lp) {
  int sign = 1;
  long l;

  const long upper_boundary = LONG_MAX / 10;
  long last_digit_max = LONG_MAX % 10;

  if (str >= *end) return ASN_STRTOL_ERROR_INVAL;

  switch (*str) {
    case '-':
      last_digit_max++;
      sign = -1;
    case '+':
      str++;
      if (str >= *end) {
        *end = str;
        return ASN_STRTOL_EXPECT_MORE;
      }
  }

  for (l = 0; str < (*end); str++) {
    switch (*str) {
      case 0x30:
      case 0x31:
      case 0x32:
      case 0x33:
      case 0x34:
      case 0x35:
      case 0x36:
      case 0x37:
      case 0x38:
      case 0x39: {
        int d = *str - '0';
        if (l < upper_boundary) {
          l = l * 10 + d;
        } else if (l == upper_boundary) {
          if (d <= last_digit_max) {
            if (sign > 0) {
              l = l * 10 + d;
            } else {
              sign = 1;
              l = -l * 10 - d;
            }
          } else {
            *end = str;
            return ASN_STRTOL_ERROR_RANGE;
          }
        } else {
          *end = str;
          return ASN_STRTOL_ERROR_RANGE;
        }
      }
        continue;
      default:
        *end = str;
        *lp = sign * l;
        return ASN_STRTOL_EXTRA_DATA;
    }
  }

  *end = str;
  *lp = sign * l;
  return ASN_STRTOL_OK;
}

int asn_int642INTEGER(INTEGER_t *st, int64_t value) {
  uint8_t *buf, *bp;
  uint8_t *p;
  uint8_t *pstart;
  uint8_t *pend1;
  int littleEndian = 1; /* Run-time detection */
  int add;

  if (!st) {
    errno = EINVAL;
    return -1;
  }

  buf = (uint8_t *)MALLOC(sizeof(value));
  if (!buf) return -1;

  if (*(char *)&littleEndian) {
    pstart = (uint8_t *)&value + sizeof(value) - 1;
    pend1 = (uint8_t *)&value;
    add = -1;
  } else {
    pstart = (uint8_t *)&value;
    pend1 = pstart + sizeof(value) - 1;
    add = 1;
  }

  /*
   * If the contents octet consists of more than one octet,
   * then bits of the first octet and bit 8 of the second octet:
   * a) shall not all be ones; and
   * b) shall not all be zero.
   */
  for (p = pstart; p != pend1; p += add) {
    switch (*p) {
      case 0x00:
        if ((*(p + add) & 0x80) == 0) continue;
        break;
      case 0xff:
        if ((*(p + add) & 0x80)) continue;
        break;
    }
    break;
  }
  /* Copy the integer body */
  for (pstart = p, bp = buf, pend1 += add; p != pend1; p += add) *bp++ = *p;

  if (st->buf) FREEMEM(st->buf);
  st->buf = buf;
  st->size = bp - buf;

  return 0;
}

asn_comp_rval_t *INTEGER_compare(asn_TYPE_descriptor_t *td1, const void *sptr1,
                                 asn_TYPE_descriptor_t *td2,
                                 const void *sptr2) {
  const INTEGER_t *st1 = (const INTEGER_t *)sptr1;
  const INTEGER_t *st2 = (const INTEGER_t *)sptr2;
  asn_comp_rval_t *res = NULL;

  COMPARE_CHECK_ARGS(td1, td2, sptr1, sptr2, res)

  if (st1->size != st2->size) {
    res = calloc(1, sizeof(asn_comp_rval_t));
    res->name = td1->name;
    res->structure1 = sptr1;
    res->structure2 = sptr2;
    res->err_code = COMPARE_ERR_CODE_NO_MATCH;
    return res;
  }
  if (0 != memcmp(st1->buf, st2->buf, st1->size)) {
    res = calloc(1, sizeof(asn_comp_rval_t));
    res->name = td1->name;
    res->structure1 = sptr1;
    res->structure2 = sptr2;
    res->err_code = COMPARE_ERR_CODE_NO_MATCH;
    return res;
  }
  return NULL;
}
